#include "Game.h"
#include <stdio.h>
#include <assert.h>


void monsterRefresh(Jeu * pJeu, float interval);
void teleportDo(Jeu * pJeu);
int abs(int x);

/* Initialisation du Jeu */
void jeuInit(Jeu * pJeu, const char option){

    int nbmonstre,nbteleport;
    int x,y;

/* On peut initialiser le jeu en 2 modes differentes */
    switch(option){
        case 'c' :
	    herInit(&(pJeu->her));
            herInitContiue(&(pJeu->her));
	    terInit(&(pJeu->ter),herGetTerID(&(pJeu->her)));
	    herGetPos(&(pJeu->her), &x, &y);
	    terModif(&(pJeu->ter),x,y,1);
            break;
	case 'n' :
	    herInit(&(pJeu->her));
	    herInitNewGame(&(pJeu->her));
	    herInitContiue(&(pJeu->her));
	    terInit(&(pJeu->ter),herGetTerID(&(pJeu->her)));
	    herGetPos(&(pJeu->her), &x, &y);
	    terModif(&(pJeu->ter),x,y,1);
	    break;
    }

    pJeu->tabmonst=monsterTabInit(&nbmonstre,herGetTerID(&(pJeu->her))); /* Initialisation du tableau des monstres */
    pJeu->tabTeleport=fillTeleport(&nbteleport); /* Initialisation du tableau des teleports */

    pJeu->nb_teleport=nbteleport;
    pJeu->nb_monstre=nbmonstre;

    pJeu->gamestat = 1; /* On commence le jeu */
    pJeu->regen_cooldown = 3.0f; /* Variable de la regeneraton des MP et HP */

}

/* Liberation du Jeu */
void jeuFree(Jeu * pJeu){

   /* On libere toute la memoire alloue */
    terFree(&(pJeu->ter));
    herFree(&(pJeu->her));
    freeTeleport(pJeu->tabTeleport);
    monsterFree(pJeu->tabmonst,pJeu->nb_monstre);


}

/* On gere les touches */
void jeuActionClavier(Jeu * pJeu, const char touche){


    switch(touche){
/* Deplacements du hero */
        case 'z' :
            heroHaut(&(pJeu->her),&(pJeu->ter));
            break;

        case 's' :
            heroBas(&(pJeu->her),&(pJeu->ter));
            break;

        case 'q' :
            heroGauche(&(pJeu->her),&(pJeu->ter));
            break;

        case 'd' :
            heroDroite(&(pJeu->her),&(pJeu->ter));
            break;
/* Attack du hero */
	case 'a' :
	    if(pJeu->her.mode==2){
            if(pJeu->her.attaque) herAttackEnd(&(pJeu->her)); else herAttackStart(&(pJeu->her));
            }
	    break;
/* Spells du hero */
    	case 'r' :
	    completeCastSpell(pJeu,1);
            break;
   	case 't' :
	    completeCastSpell(pJeu,2);
            break;
        case 'y' :
	    completeCastSpell(pJeu,3);
            break;
        case 'u' :
	    completeCastSpell(pJeu,4);
            break;
/* Changement du cible */
        case 'e' :
            dynTabPermutation(herGetTargetList(&pJeu->her));
            break;

    }
}


/* Evolution de jeu a chaque passage de boucle generale */
void jeuEvolue(Jeu * pJeu, float interval){
	int x,y;


	/* Le hero meurt si il y a 0 hp */
	if(herGetcurhp(&(pJeu->her)) == 0){

		herGetPos(&(pJeu->her), &x, &y);
		terModif(&(pJeu->ter),x,y,0);
		herDie(&(pJeu->her));
        	herInitContiue(&(pJeu->her));
		herGetPos(&(pJeu->her), &x, &y);
	        terModif(&(pJeu->ter),x,y,1);
	}

		monsterRefresh(pJeu,interval); /* Deplacement des monstres + interraction avec le hero*/
		monstAttaqueAutomatique(pJeu,interval); /* Attack des monstres */
		herAttaqueAutomatique(pJeu,interval); /* Attack automatique du hero */
		regenHeroGame(pJeu,interval); /* Regeneration du HP et MANA */
		spellRefresh(herGetSpells(&(pJeu->her)),herGetNbSpells(&(pJeu->her)),interval); /* On gere les spells */
		teleportDo(pJeu);	/* On effectue un teleport si il faut */
}


void teleportDo(Jeu * pJeu){

	int i,posh_x,posh_y;

	herGetPos(&(pJeu->her),&posh_x,&posh_y);

	for(i=0;i<pJeu->nb_teleport;i++){

	/* teste si l'hero est dans la position du teleport */
	if(posh_x == getTeleportTerOutX(&(pJeu->tabTeleport[i])) &&
	   posh_y == getTeleportTerOutY(&(pJeu->tabTeleport[i])) &&
	   herGetTerID(&(pJeu->her)) == getTeleportTerOut(&(pJeu->tabTeleport[i]))){

	/* On change la position de notre hero  */
	herNewPosition(&(pJeu->her),getTeleportTerInX(&(pJeu->tabTeleport[i])),
			getTeleportTerInY(&(pJeu->tabTeleport[i])),getTeleportTerIn(&(pJeu->tabTeleport[i])));
	jeuFree(pJeu);
	jeuInit(pJeu,'c');


	}


	}



}

/*   */
void monsterRefresh(Jeu * pJeu, float interval){

	int i,x,y,mobpresent,p;

	mobpresent=0;

	for(i=0;i<pJeu->nb_monstre;i++){


	if(monsterGetMode(&(pJeu->tabmonst[i])) != M_DEAD){

		if(getDistance(&(pJeu->tabmonst[i]),&(pJeu->her),&x,&y)<=5){	/* Si la distance entre le mob et le hero <= 5 le monstre devient notre cible */
			/*  On passe l'hero et le monstre au mode combat */

			herSetMode(&(pJeu->her),H_COMBAT);
			monsterSetMode(&(pJeu->tabmonst[i]),M_COMBAT);


           	if(!testElementAppartienTabDyn(herGetTargetList(&pJeu->her),&pJeu->tabmonst[i])) /* Si il n'est pas encore dans notre liste des cibles on l'ajoute */
                	ajouterElementTabDyn(herGetTargetList(&pJeu->her),&(pJeu->tabmonst[i]));

			mobpresent=1;
			monsterDirectionHero(pJeu,&pJeu->tabmonst[i]); /* On bouge le monstre vers hero */
		}else{ /* Si le monstre n'est pas a cote de nous */
		    /* On le passe au mode simple et si il est dans notre liste des cibles on l'a supprime */
		    monsterSetMode(&(pJeu->tabmonst[i]),M_ALIVE);
                    p=dynTabPositionElement(herGetTargetList(&pJeu->her),&pJeu->tabmonst[i]);
                    if(p!=-1) supprimerElementTabDyn(herGetTargetList(&pJeu->her),p);

            	}
		/* On diminus l'interval de deplacement et on deplace le monstre si on peut */
		monsterSetCooldownMove(&(pJeu->tabmonst[i]),interval);
		monsterMove(&(pJeu->tabmonst[i]),&(pJeu->ter));


	}else{
			/* Si monstre est vivant on verifie si c'est pas le temps du respawn */
			monsterNeedRespawn(&(pJeu->tabmonst[i]),interval);

	}


			if(!mobpresent && herGetMode(&(pJeu->her)) != H_ALIVE){ /*si le hero n'as pas de monstre a coté de lui*/
			herSetMode(&(pJeu->her),H_ALIVE);
			}

	}

    herSetTarget(&(pJeu->her),valeurIemeElementTabDyn(herGetTargetList(&(pJeu->her)),0));


}

/* La distance entre le monstre et hero */
int getDistance(Monster * pMonster,Hero * pHero, int * x,int * y){

    int tempx,tempy,tempx_mon,tempy_mon,tempx_her,tempy_her;

	monsterGetPos(pMonster,&tempx_mon,&tempy_mon);
	herGetPos(pHero,&tempx_her,&tempy_her);

	*x = tempx_mon - tempx_her;
	*y = tempy_mon - tempy_her;

	tempx = *x;
	tempy = *y;
	if (tempx<0) tempx = -(tempx);
	if (tempy<0) tempy = -(tempy);
	return tempx+tempy;

}

/* Attaque automatique du monstre */
void monstAttaqueAutomatique(Jeu *pJeu,float time){

	int i,x,y;

	for(i=0;i<pJeu->nb_monstre;i++){
		if(monsterGetMode(&(pJeu->tabmonst[i])) == M_COMBAT && getDistance(&(pJeu->tabmonst[i]),&(pJeu->her),&x,&y)<=1){ /* Montre peut vous attacker si vous etes a cote de lui */
			if(monsterGetCooldownAttack(&(pJeu->tabmonst[i]))-time <= 0){ /* Si c'est le temps d'attaquer on attaque sinon on diminue l'horloge */
				herTakeDamage(&(pJeu->her),monsterAttack(&(pJeu->tabmonst[i])));
				monsterSetCooldownAttackAbs(&(pJeu->tabmonst[i]),(float) 1 / monsterGetSpeed(&(pJeu->tabmonst[i])));
			}
			else monsterSetCooldownAttack(&(pJeu->tabmonst[i]),time);
		}
	}
}

/* Attaque automatique du hero */
void herAttaqueAutomatique(Jeu * pJeu,float time){

    int x,y,dir;

    if(pJeu->her.attaque){ /* Si attaque est declenche ou pas */

        if(pJeu->her.target != NULL){

            if(getDistance(pJeu->her.target,&(pJeu->her),&x,&y)<=1) { /* Si on est a cote du monstre */
			dir = herGetDir(&(pJeu->her)); /* direction du hero */
                if((x==-1 && dir == GAUCHE) || (x==1 && dir == DROITE) || (x==0 && y==-1 && dir == HAUT) || (x==0 && y==1 && dir == BAS)){

                    if(herGetCooldown(&(pJeu->her))<=0){
                        herAttack(&pJeu->her);
                        if(monsterGetMode(herGetTarget(&(pJeu->her)))==1){
                            monsterDie(pJeu,herGetTarget(&(pJeu->her)));
                            herAttackEnd(&(pJeu->her));
			}
			herSetCooldownAbs(&(pJeu->her),herGetSpeed(&(pJeu->her)));
                    }
                }

                herSetCooldown(&(pJeu->her),time);
            }
        }
    }
}

void completeCastSpell(Jeu * pJeu,int i){

    if(herGetNbSpells(&(pJeu->her)) >= i){ /*test que le hero possede bien le spell demandé*/
        if (checkSpell(pJeu,herGetSpell(&(pJeu->her),i))) {
            castSpell(pJeu,herGetSpell(&(pJeu->her),i));
        }
    }

}

/* Fonction qui gère le cast d'un spell */
void castSpell(Jeu * pJeu,Spell * pSpell){

    int i,x,y;
    if(spellGetNeedtarget(pSpell)){
        switch(spellGetSchooldamage(pSpell)){

            case PHYSICAL : monsterTakeDamage(herGetTarget(&(pJeu->her)),spellGetBasedamage(pSpell) + 0.5 * herGetPower(&(pJeu->her)));
                            if(monsterGetMode(herGetTarget(&(pJeu->her))) == M_DEAD){
                                monsterDie(pJeu,herGetTarget(&(pJeu->her)));
                            }

                            break;
            case MAGIC :   monsterTakeDamage(herGetTarget(&(pJeu->her)),spellGetBasedamage(pSpell) + 0.5 * herGetIntel(&(pJeu->her)));
                            if(monsterGetMode(herGetTarget(&(pJeu->her))) == M_DEAD){
                               monsterDie(pJeu,herGetTarget(&(pJeu->her)));
                            }

                            break;
        }

    }
    else {

        for(i=0;i<pJeu->nb_monstre;i++){

            if(getDistance(&(pJeu->tabmonst[i]),&pJeu->her,&x,&y) <= spellGetRadius(pSpell)){
                monsterTakeDamage(&(pJeu->tabmonst[i]),spellGetBasedamage(pSpell) + 0.5 * herGetIntel(&(pJeu->her)));

                if(monsterGetMode(&(pJeu->tabmonst[i])) == M_DEAD){
                    monsterDie(pJeu,&pJeu->tabmonst[i]);
            }
        }
    }
    }
    spellSetCooldown(pSpell);
    herUseMana(&pJeu->her,spellGetManacost(pSpell));



}



int checkSpell(Jeu * pJeu,const Spell * pSpell){ /*fonction qui autorise le cast d'un spell en fonction de :*/

    int x,y;


        if(spellGetCurcooldown(pSpell)<=0){ /* cooldown du spell*/

            if(herGetcurmp(&(pJeu->her))>= spellGetManacost(pSpell)){ /*le mana du hero*/

                if(spellGetNeedtarget(pSpell)){ /*si le spell need une cible il faut que le hero aie une target*/

                    if(herGetTarget(&pJeu->her) != NULL){

                        if(getDistance(herGetTarget(&(pJeu->her)),&(pJeu->her),&x,&y)<=spellGetRange(pSpell)){
                            return 1;       /*si la target est a portée du spell*/
                        }
                    }
                }
                else return 1; /*si le spell n'a pas besoin de target, le spell sera lancé.*/
            }
        }

    return 0;
}



int statGame(const Jeu * pJeu){
	return pJeu->gamestat;
}

void setGame(Jeu * pJeu,int stat){
	pJeu->gamestat = stat;
}


void regenHeroGame(Jeu * pJeu, float time){

  int addhp,addmana;

  if(herGetMode(&(pJeu->her)) == 0){
	addhp = herGethpmax(&(pJeu->her)) * 0.07;
	if(addhp < 1) addhp = 1;
	addmana = herGetmpmax(&(pJeu->her)) * 0.2;
	if(addmana < 1) addmana = 1;

	if(pJeu->regen_cooldown <= 0){

		herWinMana(&(pJeu->her),addmana);
		herWinHp(&(pJeu->her),addhp);

		pJeu->regen_cooldown = 3.0f;

	}

	pJeu->regen_cooldown  -= time;
  }

}

void monsterDie(Jeu * pJeu,Monster * pMonster){
    int p,x,y;

    monsterGetPos(pMonster, &x, &y);
    herWinXp(&pJeu->her,monsterGetExp(pMonster));
    terModif(&(pJeu->ter),x,y,0);

    p=dynTabPositionElement(herGetTargetList(&pJeu->her),pMonster);
        if(p!=-1) supprimerElementTabDyn(herGetTargetList(&pJeu->her),p);


}


void monsterDirectionHero(Jeu * pJeu,Monster * pMonster){
    int x,y;

   getDistance(pMonster,&(pJeu->her),&x,&y);

   if(abs(x)<abs(y)){
     if(y>0) monsterSetDirection(pMonster,2);
     else monsterSetDirection(pMonster,3);

   }
   else {

       if(x>0) monsterSetDirection(pMonster,0);
       else monsterSetDirection(pMonster,1);
}
}


int abs(int x){ /*valeur absolue*/

    if(x<0) return -x;
    else return x;

}

void jeuTestRegression(){

	Jeu pJeu;
	int temp_x,temp_y,rec_x,rec_y;
	printf("****** TEST GAME START ************ \n");
	jeuInit(&pJeu,'c');
		
	monsterGetPos(&(pJeu.tabmonst[0]), &rec_x, &rec_y);

	temp_x = rec_x;
	temp_y = rec_y;
	
	jeuEvolue(&pJeu,1);
	jeuEvolue(&pJeu,1);
	jeuEvolue(&pJeu,1);

	monsterGetPos(&(pJeu.tabmonst[0]), &rec_x, &rec_y);

	if(pJeu.nb_monstre != 0 && pJeu.nb_teleport != 0 && pJeu.gamestat == 1 && !(rec_x == temp_x && rec_y == temp_y)){
	printf("Module travaille correctement....\n");
	}else{
	printf("Attention! Module erreur.....\n");
	}

	jeuFree(&pJeu);

	printf("****** TEST GAME END ************** \n");

}
